#include "TouchScreen.h"
#include <process.h>

#ifndef DLL_FILE
#define DLL_FILE
#endif

const char* config_coor = "coor.config";
const char* config_filter = "filter.config";

TouchScreen::TouchScreen(void)
{
	hThread = 0;
	eventListMutex = CreateMutex(NULL, 0, NULL);

	calib.init_from_file (config_coor);
	init_filter_factory ();

}

void TouchScreen::process(void)
{
	size_t i,n;
	Filter* f;
  // Initial capture from camera
	cvNamedWindow( "mywindow", CV_WINDOW_AUTOSIZE );
	CvCapture* capture = cvCaptureFromCAM( CV_CAP_ANY );
	//CvCapture* capture = cvCaptureFromAVI("try_01.avi");
	IplImage* frame = cvQueryFrame( capture );
	IplImage* process_img = cvCreateImage(cvSize(frame->width,frame->height),8,1);

	while(true)
  {

		// Get a frame
		if((frame = cvQueryFrame (capture)) == NULL)
			break;

    // Convert to gray image
		cvCvtColor(frame,process_img,CV_BGR2GRAY);

    // Filter process
		n = filterFac.filter_chain.size();
		for(i=0;i<n;i++){
			f = filterFac.filter_chain[i];
			if(f->active()){
				f->process(process_img,process_img);
			}
		}

    // Detect touch points
		vector<TouchData> tdata_list = tracker.process (process_img);

    // Convert to coord on globe
		for (i = 0; i < tdata_list.size (); i++)
			cam_to_world (tdata_list[i]);

    // Tag the points detected
    for (int i = 0;i < tdata_list.size (); ++i)
      cvCircle (process_img, tdata_list[i].getCenter (), 10, cvScalar (255,0,0));

    // Send detected result
    if (tdata_list.size () > 0)
			fire_event(tdata_list);

		cvShowImage( "mywindow", process_img );

		if( (cvWaitKey(10) & 255) == 27 ) break;
	}

}

void TouchScreen::cam_to_world (TouchData &data)
{
	vector2dd pos (data.X, data.Y);
	vector2dd last_pos (data.lastX, data.lastY);

	vector2dd vec = calib.transform_coor (pos);
	data.sphere_longitude = vec.X;
	data.sphere_latitude = vec.Y;

	vec = calib.transform_coor (last_pos);
	data.last_sph_log = vec.X;
	data.last_sph_lat = vec.Y;
}

void TouchScreen::fire_event(const std::vector<TouchData> &touch_data)
{
	//printf("action\n");
	DWORD dw = WaitForSingleObject(eventListMutex, INFINITE);
	//dw == WAIT_OBJECT_0
	if(dw == WAIT_TIMEOUT || dw == WAIT_FAILED) {
		// handle time-out error
		//throw TimeoutExcp();
		printf("Failed %d", dw);
	}else{
		int i;
		for(i=0;i<listeners.size();i++){
			listeners[i]->touch_event(touch_data);
		}
		ReleaseMutex(eventListMutex);
	}
}

void TouchScreen::start_process(void)
{
	#ifdef WIN32
		hThread = (HANDLE)_beginthread(_processEntryPoint, 0, this);
		SetThreadPriority(hThread, THREAD_PRIORITY_ABOVE_NORMAL);
	#else
		pthread_create(&hThread,0,_processEntryPoint,this);
	#endif
}

THREAD_RETURN_TYPE TouchScreen::_processEntryPoint (void * obj)
{
	((TouchScreen *)obj)->process();
}

void TouchScreen::init_calibration (const char* filename)
{
	calib.init_from_file (filename);
}

void TouchScreen::init_filter_factory ()
{
	if (!filterFac.load_filter_chain_from_file (config_filter))
		filterFac.load_full_default_filter ();
}

TouchScreen::~TouchScreen(void)
{
	if(hThread)
		TerminateThread(hThread, 0);
	
	CloseHandle(eventListMutex);
}